package gateway

import (
	"context"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/spf13/cast"
	"log"
	"net/http"
	"server/component"
	"server/im/entity"
)

func NewWebSocketServer(
	ctx context.Context,
	config component.ImServerConfig,
) GateWay {

	s := &WebSocketServer{BaseGateway: BaseGateway{
		ctx:               ctx,
		GatewayUni:        config.Uni,
		Config:            config,
		ClientMap:         make(map[string]*ClientItem),
		ClientChan:        make(chan entity.GatewayClientEvent),
		CoreInputChan:     make(chan entity.CoreInModel, 1024),
		CoreOutputChan:    make(chan entity.CoreOutModel, 1024),
		GatewayInputChan:  make(chan entity.GatewayInModel, 1024),
		GatewayOutputChan: make(chan entity.GatewayOutModel, 1024),
		BroadcastChan:     make(chan entity.GatewayBroadcastModel, 1024),
		exitChan:          make(chan struct{}),
	}}

	s.FnStartListen = s.doStartListen
	s.FnStopListen = s.doStopListen
	return s
}

type WebSocketServer struct {
	BaseGateway
	stopListen bool
	server     http.Server
}

func (o *WebSocketServer) doStopListen() {
	fmt.Println("ws do stop listen")
	err := o.server.Close()
	if err != nil {
		component.Log.Error(err)
	}
	component.Log.Info(o.GatewayUni + "listen close")
}

func (o *WebSocketServer) doStartListen() {

	http.HandleFunc("/ws", o.wsHandler)
	port := o.Config.Port
	o.server = http.Server{Addr: ":" + port}
	// 启动服务器
	fmt.Printf("ws %s listen on:%s \n", o.GetUniId(), port)
	err := o.server.ListenAndServe()
	if err != nil {
		fmt.Errorf("web socket start err %w", err)
	}
	fmt.Println("ws listen finish")
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func (o *WebSocketServer) wsHandler(w http.ResponseWriter, r *http.Request) {
	if o.stopListen == true {
		w.Write([]byte("server stop"))
		return
	}
	// 升级HTTP连接为WebSocket连接
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}
	socketConn := WebSocketConn{
		conn:    conn,
		gateway: o,
	}
	o.Conn2Client(&socketConn)
}

type WebSocketConn struct {
	conn    *websocket.Conn
	gateway GateWay
}

func (o *WebSocketConn) GetGateWay() GateWay {
	return o.gateway
}

func (o *WebSocketConn) DoRead(readChan chan entity.GatewayInModel, errChan chan error, stopChan chan struct{}) {
	defer func() {
		fmt.Println("ws read over")
		if err := recover(); err != nil {
			component.Log.Error(err)
		}
	}()
	for {
		select {
		case <-stopChan:
			fmt.Println("ws read break")
			return
		default:
			_, p, err := o.conn.ReadMessage()
			if err != nil {
				errChan <- err
				break
			}
			fmt.Println("gateway", p)
			fmt.Println("gateway string", string(p))
			inputMap := make(map[string]any)
			err = component.JsonDecode(p, &inputMap)
			if err != nil {
				errChan <- err
				break
			}
			cmdStr, ok := inputMap["cmd"]
			if !ok {
				errChan <- errors.New("no cmd")
				break
			}
			cmd := cast.ToUint16(cmdStr)
			if cmd == 0 {
				errChan <- errors.New("cmd err")
				break
			}

			delete(inputMap, "cmd")
			var msgBytes []byte
			if len(inputMap) > 0 {
				msgBytes, err = component.JsonEncode(&inputMap)
				if err != nil {
					errChan <- errors.New("input err")
					break
				}
			}

			GatewayInModel := entity.GatewayInModel{
				Data: msgBytes,
				Cmd:  cmd,
				Ip:   o.conn.RemoteAddr().String(),
			}
			readChan <- GatewayInModel
		}
	}
}

func (o *WebSocketConn) WriteFn(writeModel entity.GatewayOutModel) {
	bytes, err := component.JsonEncode(writeModel)
	if err != nil {
		component.Log.Error(err)
		return
	}
	o.conn.WriteMessage(websocket.TextMessage, bytes)
}

func (o *WebSocketConn) CloseFn() {
	o.conn.Close()
}
